require 'rubygems'
require 'erb'
require 'open-uri'
require 'fileutils'

# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('./Gemfile', File.dirname(__FILE__))
require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
Bundler.require(:default, :test) if defined?(Bundler)

project_dir = File.expand_path('..', File.dirname(__FILE__))
base_path = File.expand_path('pushstream/docs/preview', Dir.tmpdir)
javascript_dir = File.expand_path(Dir["#{project_dir}/**/js"].first)

begin
  require "rspec/core/rake_task"

  desc "Run all examples"
  RSpec::Core::RakeTask.new(:spec) do |t|
    t.rspec_opts = %w[--color]
    t.pattern = '**/*_spec.rb'
  end
rescue LoadError
  task :spec do
    abort "RSpec is not available. In order to run rspec, you must: (sudo) gem install rspec"
  end
end

begin
  load 'jasmine/tasks/jasmine.rake'

  task "jasmine:require" => ["jshint", "configure_jasmine", "monitor_js", "test_server"]

  task :configure_jasmine do
    Jasmine.configure do |config|
      config.spec_dir = project_dir
      config.spec_files = lambda { Dir["#{project_dir}/misc/spec/javascripts/helpers/**/*.js"] + Dir["#{project_dir}/misc/js/jquery.min.js"] + Dir["#{project_dir}/misc/**/*[sS]pec.js"] }
      js_tmp_dir = File.expand_path('pushstream/js', Dir.tmpdir)
      config.src_dir = js_tmp_dir
      config.src_files = lambda { Dir["#{js_tmp_dir}/**/*.js"] }
    end
  end

  task :monitor_js do
    def copy_inner_js(modified, added, removed)
      modified.each do |file|
        destiny_path = File.dirname(file).gsub(/.*\/js\/?/, File.expand_path('pushstream/js', Dir.tmpdir))
        FileUtils.mkdir_p(destiny_path)
        content = File.read file
        content.gsub!('window.PushStream = PushStream;', "window.PushStream = PushStream;\nwindow.Utils = Utils;")
        File.open(File.join(destiny_path, File.basename(file)), 'w') {|f| f.write content }
      end
    end

    copy_inner_js([File.expand_path('misc/js/pushstream.js', project_dir)], [], [])
    listener = Listen.to(File.expand_path('misc/js', project_dir), :filter => /\.js$/) do |modified, added, removed|
      copy_inner_js(modified, added, removed)
    end
    listener.start
  end

  task :test_server do
    require File.expand_path('misc/spec/spec_helper', project_dir)
    include NginxTestHelper
    template = File.read(File.expand_path('misc/nginx.conf', project_dir))
    template.gsub!(/push_stream_subscriber_connection_ttl.*;/, 'push_stream_subscriber_connection_ttl 3s;')
    template.gsub!(/push_stream_longpolling_connection_ttl.*;/, 'push_stream_longpolling_connection_ttl 3s;')
    config = NginxTestHelper::Config.new "jasmine", {:configuration_template => (RUBY_PLATFORM =~ /darwin/) ? template.gsub('epoll', 'kqueue') : template }
    abort "Could not start test server" if start_server(config).include?("[emerg]")
  end

rescue LoadError
  desc "Run javascript tests"
  task :jasmine do
    abort "Jasmine is not available. In order to run jasmine, you must: (sudo) gem install jasmine"
  end
end

begin
  require "jshintrb/jshinttask"
  Jshintrb::JshintTask.new :jshint do |t|
    t.pattern = "#{javascript_dir}/pushstream.js"
    t.options = :defaults
  end
rescue LoadError
  desc "Run jshint on js"
  task :jshint do
    abort "Jshintrb is not available. In order to run jshint, you must: (sudo) gem install jshintrb"
  end
end

namespace :docs do

begin
  Bundler.require(:default, :docs) if defined?(Bundler)

  task :get_static_files do
    FileUtils.mkdir_p("#{base_path}/css")

    download_file("https://github.githubassets.com/assets/frameworks-a3b8a10d4a9e37a78f033ef4a4f525f5.css", "#{base_path}/css/github.css") unless File.exists?("#{base_path}/css/github.css")
    download_file("https://github.githubassets.com/assets/github-6aa34635057b47b21a5f5ae68b399802.css", "#{base_path}/css/github2.css") unless File.exists?("#{base_path}/css/github2.css")
  end

  def generate_preview_for(file, project_dir, base_path)
    template = ERB.new File.read("#{project_dir}/misc/github_template.html.erb")
    filename = File.basename(file)
    content = GitHub::Markup.render(file, File.read(file))
    rendered = template.result(binding)
    output = File.expand_path(file.gsub(project_dir, "./"), base_path)
    output_dir = File.dirname(output)
    FileUtils.mkdir_p(output_dir)
    File.open(output, 'w') {|f| f.write(rendered) }
    puts "Preview rendered to #{output}"
  end

  desc "Generates docs files to preview."
  task :generate => :get_static_files do
    Dir.glob("#{project_dir}/**/*.textile").each do |file|
      generate_preview_for(file, project_dir, base_path)
    end
  end

  desc "Watch for changes on doc to generate the preview."
  task :watch do
    puts "watching for changes on textile files"
    Dir.chdir(project_dir) do
      FileWatcher.new(["**/*.textile"]).watch do |file, event|
        if(event != :delete)
          generate_preview_for(file, project_dir, base_path)
        end
      end
    end
  end

  desc "Convert docs to Nginx wiki format."
  task :convert_to_wiki do
    Dir.glob("#{project_dir}/**/*.textile").each do |file|
      filename = File.basename(file)
      content = File.read(file)

      output      = file.gsub(project_dir, File.expand_path('pushstream/docs/html', Dir.tmpdir)).gsub(".textile", ".html")
      output_wiki = file.gsub(project_dir, File.expand_path('pushstream/docs/wiki', Dir.tmpdir)).gsub(".textile", ".wiki")
      FileUtils.mkdir_p(File.dirname(output))
      FileUtils.mkdir_p(File.dirname(output_wiki))

      File.open(output, 'w') {|f| f.write(RedCloth.new(content).to_html) }
      File.open(output_wiki, 'w') {|f| f.write(convert_to_wiki_syntax(content)) }
      puts "Wiki converted to #{output_wiki}"
    end
  end
rescue LoadError
  desc "Generates docs files to preview."
  task :generate do
    abort "github-markup is not available. In order to run docs:generate, you must: (sudo) gem install github-markup"
  end

  desc "Convert docs to Nginx wiki format."
  task :convert_to_wiki do
    abort "RedCloth or nokogiri is not available. In order to run docs:convert_to_wiki, you must: (sudo) gem install RedCloth nokogiri"
  end
end

  def download_file(url, output_file)
    case io = open(url)
    when StringIO then File.open(output_file, 'w') { |f| f.write(io) }
    when Tempfile then io.close; FileUtils.mv(io.path, output_file)
    end
  end

  def convert_to_wiki_syntax(text)
    doc = Nokogiri::HTML(RedCloth.new(text).to_html)
    convert_elements(doc.children.to_a)
  end

  def convert_elements(nodes)
    result = ""
    nodes.each do |node|
      if node.element? && !node.text?
        childrens = node.children.to_a
        unless childrens.empty?
          result += convert_element(convert_elements(childrens), node)
        end
      elsif node.text?
        result += node.text
      end
    end
    result
  end

  def convert_element(text, node)
    tag = node.name
    text ||= ""
    case tag
    when "strong"
      "'''#{text}'''"
    when "b"
      "'''#{text}'''"
    when "em"
      "''#{text}''"
    when "h1"
      "\n= #{text} ="
    when "h2"
      "\n== #{text} =="
    when "h3"
      "\n=== #{text} ==="
    when "h4"
      "\n==== #{text} ===="
    when "h5"
      "\n===== #{text} ====="
    when "h6"
      "\n====== #{text} ======"
    when "p"
      "\n#{text}"
    when "a"
      if node.attributes['href'].value.start_with?("#")
        "[[#{node.attributes['href'].value}|#{text}]]"
      else
        "[#{node.attributes['href'].value} #{text}]"
      end
    when "html"
      text
    when "body"
      text
    when "span"
      text
    else
      "<#{tag}>#{text}</#{tag}>"
    end
  end
end
